/* freeircd commands.c
 *
 * Iniciado em 02/08/2006
 */

#define _GNU_SOURCE
#include <errno.h>
#include "misc.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <string.h>
#include <fcntl.h>
#include <stdbool.h>
#include <time.h>
#include <stdarg.h>
#include <signal.h>
#include "md5/md5c.c"
#include "alloc.h"

#include "types.h"
#include "configuration.h"
#include "getinfo.h"

#include "debug.h"

inline char *get_irc_str(char *, int);
bool nick(CLIENTS **, CLIENTS *, CHANS **, long*);
void ping(CLIENTS *);
void user(CLIENTS *);
void pong(CLIENTS *);
void userhost(CLIENTS **, CLIENTS *);
void whois(CLIENTS **, CLIENTS *);
void privmsg(CLIENTS **, CLIENTS *);
void notice(CLIENTS **, CLIENTS *);
void join(CLIENTS **, CLIENTS *, long *, CHANS **);
void who(CLIENTS *);
void names(CLIENTS *);
void part(CLIENTS *, CHANS **, bool, long *, bool);
void oper(CLIENTS *, long *, IRCOPS *);
void topic(CLIENTS *, CHANS *);
void ison(CLIENTS **, CLIENTS *);
void list(CLIENTS *, CHANS *);
void kill_(CLIENTS **, CLIENTS *);
void kick(CLIENTS *, CHANS **, CLIENTS **, long *);
void away(CLIENTS *);
void help(CLIENTS *);
void invite(CLIENTS *, CLIENTS *);
void die(CLIENTS *, CLIENTS **, IRCOPS *);
void mode(CLIENTS *, CHANS *,long *);
void wallops(CLIENTS *, CLIENTS *);

char *strip_msg(char *);

extern void write_sock(CLIENTS *, char *, ...);
extern void write_sock_already_form(CLIENTS *, char *);
extern inline void memerror(void *);
extern void user_modes(CLIENTS *, long *);
extern void chan_modes(CLIENTS *, CHANS *);
extern bool is_banned(CLIENTS *, CHANS *);
extern void check_chans(CHANS **, long *);
extern void save_userreg(USERREG *);
extern veryshort get_user_access_level(struct access_users *, char *);
extern bool is_akicked(CLIENTS *, struct chan_akicks *);
extern void send_to_all_in_chan(CLIENTS *, char *, char *, ...);
extern void send_to_all_in_chan_already_form(CLIENTS *, char *, char *);
extern void send_to_all(CLIENTS *, char *, ...);

inline char *get_irc_str(char *str, int token)
{
        char *ret;
        int counter=0;
        int rcounter=0;
        int s_counter=1;
        bool cpy=false;
        bool chk_n=false;

        if (str == NULL)
                return NULL;

        ret=malloc(strlen(str)+1);
        memerror(ret);

        while (str[counter] != '\0')
        {
                if (str[counter] == ' ')
                {
                        s_counter++;
                        if (token == s_counter)
                        {
                                chk_n=true;
                                counter++;
                                continue;
                        }
                }

                if (chk_n)
                {
                        chk_n=false;
                        cpy=true;
                        if (str[counter] == ':')
                        {
                                counter++;
                                continue;
                        }
                }

                if (cpy)
                        ret[rcounter++]=str[counter];
                counter++;
        }

        ret[rcounter]='\0';

        return ret;
}

/*RFC 1459, page 14*/
bool nick(CLIENTS **ppclients, CLIENTS *client, CHANS **ppchans, long *channels)
{
        char *nick;
        char *tmp;
        long counter=0;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick NICK :Not enough parameters*/
                write_sock(client,":%s 461 %s NICK :Not enough parameters\r\n",SERVER_NAME,
                                                                         (client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
                return false;
        }

        nick=get_irc_str(client->msg,2);

        if (nick == NULL)
                return false;

        if (strlen(nick) > MAX_NICKNAME_LEN)
        {
                nick[MAX_NICKNAME_LEN]='\0';
                nick=realloc(nick,strlen(nick)+1);
                memerror(nick);
        }

        if (compic(nick,NICKSERV_NAME) || compic(nick,CHANSERV_NAME) || compic(nick,OPERSERV_NAME))
        {
                write_sock(client,":%s 432 %s %s :Erroneus nickname\r\n",SERVER_NAME,
                           (client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick,nick);
                free(nick);
                return false;
        }

        while (nick[counter] != '\0')
        {
                if ((nick[counter] >= 'A' && nick[counter] <= 'Z')
                    || (nick[counter] >= 'a' && nick[counter] <= 'z')
                    || (nick[counter] >= '0' && nick[counter] <= '9')
                    || nick[counter] == '`'
                    || nick[counter] == '\\'
                    || nick[counter] == '['
                    || nick[counter] == ']'
                    || nick[counter] == '^'
                    || nick[counter] == '|'
                    || nick[counter] == '{'
                    || nick[counter] == '}'
                    || nick[counter] == '_'
                    || nick[counter] == '-')
                        ;
                else
                {
                        /*:server 432 oldnick newnick :Erroneus nickname*/

                        write_sock(client,":%s 432 %s %s :Erroneus nickname\r\n",SERVER_NAME,
                                              (client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick,nick);
                        free(nick);
                        return false;
                }
                counter++;
        }

        if (get_client(*ppclients,nick) != NULL) /*Verifica se não está a ser usado*/
        {
                /*:server 433 oldnick newnick :Nickname is already in use*/
                write_sock(client,":%s 433 %s %s :Nickname is already in use\r\n",SERVER_NAME,
                        (client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick,nick);
                free(nick);
                return false;
        }

        if (comp(nick,client->nick)) /*Iguais na capitalização*/
        {
                write_sock(client,":%s 433 %s %s :Nickname is already in use\r\n",SERVER_NAME,
                           (client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick,nick);
                free(nick);
                return false;
        }

        if (client->nick != NULL)
        {
                /*:oldnick!user@server NICK :newnick*/
                write_sock(client,":%s!%s@%s NICK :%s\r\n",client->nick,USER_NAME,client->md5_ip,nick);
                send_to_all(client,":%s!%s@%s NICK :%s\r\n",client->nick,USER_NAME,client->md5_ip,nick);
        }
        else
        {
                time(&client->next_ping);
                client->next_ping--;
        }

        free(client->nick);
        client->nick=nick;

        tmp=client->msg;
        client->msg=malloc(32);
        memerror(client->msg);
        strcpy(client->msg,"PART #unregisted");
        part(client,ppchans,false,channels,true);
        strcpy(client->msg,"PART #unidentified");
        part(client,ppchans,false,channels,true);
        free(client->msg);
        client->msg=tmp;

        return true;
}

/*RFC 1459, page 37*/
void ping(CLIENTS *client)
{
        char *pong;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick PING :Not enough parameters*/
                write_sock(client,":%s 461 %s PING :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        pong=getoken(client->msg,2,' ');

        /*:server PONG server :XXX*/
        write_sock(client,":%s PONG %s :%s\r\n",SERVER_NAME,SERVER_NAME,pong);

        free(pong);
}

/*RFC 1459, page 15*/
void user(CLIENTS *client)
{
        if (tknum(client->msg,' ') < 5)
        {
                /*:server 461 nick USER :Not enough parameters*/
                write_sock(client,":%s 461 %s USER :Not enough parameters\r\n",SERVER_NAME,
                           (client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
                return;
        }

        client->name=get_irc_str(client->msg,5);
}

/*RFC 1459, page 37*/
void pong(CLIENTS *client)
{
        char *pong;
        long pong_l;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick PONG :Not enough parameters*/
                write_sock(client,":%s 461 %s PONG :Not enough parameters\r\n",SERVER_NAME,
                           (client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
                return;
        }

        pong=getoken(client->msg,2,' ');

        if (strlen(pong) < 2) {
                free(pong);
                return;
        }

        if (client->sent_ping)
                if (pong[0] == ':')
                {
                        pong_l=atol(&pong[1]);
                        if (pong_l == (long)client->sent_ping)
                        {
                                client->sent_ping=0;
                                time(&client->next_ping);
                                client->next_ping+=PING_FREQUENCY;

                                if (client->do_welcome == 1)
                                        client->do_welcome=2;
                        }
                }
        free(pong);
}

void userhost(CLIENTS **ppclients, CLIENTS *client)
{
        CLIENTS *tclient;
        char *nick;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick USERHOST :Not enough parameters*/
                write_sock(client,":%s 461 %s USERHOST :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        nick=getoken(client->msg,2,' ');

        tclient=get_client(*ppclients,nick);

        if (tclient != NULL)
                /*:server 302 nick :nick=user@server*/
                write_sock(client,":%s 302 %s :%s=%s@%s\r\n",SERVER_NAME,tclient->nick,nick,USER_NAME,tclient->md5_ip);

        free(nick);
}

void whois(CLIENTS **ppclients, CLIENTS *client)
{
        CLIENTS *pclients;
        char *nick;
        time_t tm;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick WHOIS :Not enough parameters*/
                write_sock(client,":%s 461 %s WHOIS :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        nick=getoken(client->msg,2,' ');

        pclients=get_client(*ppclients,nick);

        if (pclients != NULL)
        {       /*
                 * :irc.ptsida.org 311 |_-GoD-_| |_-GoD-_| GoD irc.ptsida.org * :God, Official #putedo bot
                 * :irc.ptsida.org 319 |_-GoD-_| |_-GoD-_| :@#putedo
                 * :irc.ptsida.org 312 |_-GoD-_| |_-GoD-_| irc.ptsida.org :A Rede dos sidosos
                 * :irc.ptsida.org 313 |_-GoD-_| PTsida :is an IRC Operator
                 * :irc.ptsida.org 317 |_-GoD-_| |_-GoD-_| 2 1154568604 :seconds idle, signon time
                 * :irc.ptsida.org 318 |_-GoD-_| |_-GoD-_| :End of /WHOIS list.
                 */

                time(&tm);
                if (!client->ircop)
                        write_sock(client,":%s 311 %s %s %s %s * :%s\r\n",SERVER_NAME,client->nick,
                                          pclients->nick,USER_NAME,client->md5_ip,pclients->name);
                else
                        write_sock(client,":%s 311 %s %s %s %s * :%s\r\n",SERVER_NAME,client->nick,
                                   pclients->nick,USER_NAME,inet_ntoa(pclients->sock.sin_addr),pclients->name);

                if (pclients->chans != NULL)
                {
                        struct user_chans_list *chan_lst=pclients->chans;

                        write_sock(client,":%s 319 %s %s :",SERVER_NAME,client->nick,pclients->nick);

                        while (chan_lst != NULL)
                        {
                                if (!chan_lst->secret) /*não e secreto, independente de ser ircop*/
                                {
                                        if (chan_lst->chan->modes.s) /*canal com modo +s*/
                                        {
                                                if (client->ircop) /*...mas o requester é ircop*/
                                                {
                                                        write_sock(client,"%c02",(char)3);
                                                        if (chan_lst->op)
                                                                write_sock(client,"@");
                                                        else if (chan_lst->voice)
                                                                write_sock(client,"+");

                                                        write_sock(client,"%s%c02 ",chan_lst->chan->name,(char)3);
                                                }
                                        }
                                        else
                                        {
                                                if (chan_lst->op)
                                                        write_sock(client,"@");
                                                else if (chan_lst->voice)
                                                        write_sock(client,"+");

                                                write_sock(client,"%s ",chan_lst->chan->name);
                                        }
                                }
                                else /*é secreto*/
                                {
                                        if (client->ircop) /*...mas o requester é ircop*/
                                        {
                                                write_sock(client,"%c04",(char)3);
                                                if (chan_lst->op)
                                                        write_sock(client,"@");
                                                else if (chan_lst->voice)
                                                        write_sock(client,"+");

                                                write_sock(client,"%s%c04 ",chan_lst->chan->name,(char)3);
                                        }
                                }

                                chan_lst=chan_lst->next;
                        }
                        write_sock(client,"\r\n");
                }

                write_sock(client,":%s 312 %s %s %s :%s\r\n",SERVER_NAME,client->nick,pclients->nick,SERVER_NAME,NETWORD_DESCRIPTION);

                if (pclients->ircop)
                        write_sock(client,":%s 313 %s %s :is an IRC Operator\r\n",SERVER_NAME,client->nick,pclients->nick);

                write_sock(client,":%s 317 %s %s %ld %ld :seconds idle, signon time\r\n",SERVER_NAME,client->nick,pclients->nick,
                                                                                 (long)tm-pclients->last_move,(long)pclients->sign_on);
                write_sock(client,":%s 318 %s %s :End of /WHOIS list.\r\n",SERVER_NAME,client->nick,pclients->nick);
        }
        else
        {
                /*:server 401 nick rnick :No such nick
                 *:server 318 nick rnick :End of /WHOIS list.
                 */
                write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,client->nick,nick);
                write_sock(client,":%s 318 %s %s :End of /WHOIS list.\r\n",SERVER_NAME,client->nick,nick);
        }

        free(nick);
}

void privmsg(CLIENTS **ppclients, CLIENTS *client)
{
        CLIENTS *pclients=*ppclients;
        char *target;
        char *msg;

        /*PRIVMSG muda :hello world*/
        if (tknum(client->msg,' ') < 3)
        {
                /*:server 461 nick PRIVMSG :Not enough parameters*/
                write_sock(client,":%s 461 %s PRIVMSG :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        msg=get_irc_str(client->msg,3);
        if (msg == NULL)
                return;

        target=getoken(client->msg,2,' ');

        if (target[0] == '#') /*é para um canal*/
        {
                struct user_chans_list *chan_lst;

                chan_lst=get_chan_from_user_list(client->chans,target);

                if (chan_lst != NULL)
                {
                        if (!chan_lst->chan->modes.m)
                                send_to_all_in_chan(client,target,":%s!%s@%s PRIVMSG %s :%s\r\n",
                                                                  client->nick,USER_NAME,client->md5_ip,target,msg);
                        else
                        {
                                if (chan_lst->op || chan_lst->voice)
                                {
                                        if (chan_lst->chan->modes.c)
                                        {
                                                char *striped;
                                                striped=strip_msg(msg);
                                                send_to_all_in_chan(client,target,":%s!%s@%s PRIVMSG %s :%s\r\n",
                                                                           client->nick,USER_NAME,client->md5_ip,target,striped);
                                                free(striped);
                                        }
                                        else
                                                send_to_all_in_chan(client,target,":%s!%s@%s PRIVMSG %s :%s\r\n",
                                                                        client->nick,USER_NAME,client->md5_ip,target,msg);
                                }
                                else  /*:irc.ptsida.org 404 coiso #coiso :Cannot send to channel*/
                                        write_sock(client,":%s 404 %s %s :Cannot send to channel\r\n",SERVER_NAME,client->nick,target);

                        }

                }
                else
                        /*:irc.ptsida.org 403 orium_ #coiso :No such channel*/
                        write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,target);
        }
        else
        {
                pclients=get_client(pclients,target);

                if (pclients != NULL)
                {
                        /*:coiso!~orium@irc.ptsida.org PRIVMSG outro :hiiii*/
                        write_sock(pclients,":%s!%s@%s PRIVMSG %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,pclients->nick,
                                                                          msg);
                        if (pclients->away != NULL)
                                write_sock(client,":%s 301 %s %s :%s\r\n",SERVER_NAME,client->nick,pclients->nick,pclients->away);
                                /*:irc.ptsida.org 301 lol nick :hi*/
                }
                else
                        /*:irc.ptsida.org 401 orium sdhfgd :No such nick*/
                        write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,client->nick,target);
        }

        free(msg);
        free(target);
}

void notice(CLIENTS **ppclients, CLIENTS *client)
{
        CLIENTS *pclients=*ppclients;
        char *target;
        char *msg;

        /*NOTICE nick :hello world*/
        if (tknum(client->msg,' ') < 3)
        {
                /*:server 461 nick NOTICE :Not enough parameters*/
                write_sock(client,":%s 461 %s NOTICE :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        msg=get_irc_str(client->msg,3);
        if (msg == NULL)
                return;

        target=getoken(client->msg,2,' ');

        if (target[0] == '#') /*é para um canal*/
        {
                struct user_chans_list *chan_lst;

                chan_lst=get_chan_from_user_list(client->chans,target);

                if (chan_lst != NULL)
                        send_to_all_in_chan(client,target,":%s!%s@%s NOTICE %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,target,msg);
                else
                        /*:irc.ptsida.org 403 orium_ #coiso :No such channel*/
                        write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,target);
        }
        else
        {
                pclients=get_client(pclients,target);

                if (pclients != NULL)
                {
                        /*:coiso!~orium@irc.ptsida.org NOTICE outro :hiiii*/
                        write_sock(pclients,":%s!%s@%s NOTICE %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,pclients->nick,msg);

                        if (pclients->away != NULL)
                                write_sock(client,":%s 301 %s %s :%s\r\n",SERVER_NAME,client->nick,pclients->nick,pclients->away);
                        /*:irc.ptsida.org 301 lol nick :hi*/
                }
                else
                        /*:irc.ptsida.org 401 orium sdhfgd :No such nick*/
                        write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,client->nick,target);
        }
        free(msg);
        free(target);
}

void join(CLIENTS **ppclients, CLIENTS *client, long *channels, CHANS **ppchans)
{
        CHANS *pchans;
        char *chan_name;
        char *key;
        long counter=0;
        bool secret=false;
        struct chan_users_list *pcusers;
        struct user_chans_list *puchans;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick JOIN :Not enough parameters*/
                write_sock(client,":%s 461 %s JOIN :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        chan_name=getoken(client->msg,2,' ');

        if (comp(chan_name,"-s") && client->ircop)
        {
                if (tknum(client->msg,' ') < 3)
                {
                        /*:server 461 nick JOIN :Not enough parameters*/
                        write_sock(client,":%s 461 %s JOIN :Not enough parameters\r\n",SERVER_NAME,client->nick);
                        return;
                }
                free(chan_name);
                chan_name=getoken(client->msg,3,' ');
                secret=true;
        }

        if (chan_name[0] != '#' || strlen(chan_name) > MAX_CHANNEL_LEN)
        {
                /*:server 461 nick JOIN :Not enough parameters*/
                write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,chan_name);
                free(chan_name);
                return;
        }

        while (chan_name[counter] != '\0')
        {
                switch (chan_name[counter])
                {
                        case '\7': ;
                        case ' ': ;
                        case ',': write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,chan_name);
                                  free(chan_name);
                                  return;
                }
                counter++;
        }

        if (!client->ircop && get_quant_chans_of_nick(client->chans) > MAX_CHANS_PER_USER)
        {       /*:irc.ptsida.org 405 xxx #gdfgjh6 :you have joined too many channels*/
                write_sock(client,":%s 405 %s %s :You have joined too many channels\r\n",SERVER_NAME,client->nick,chan_name);
                free(chan_name);
                return;
        }

        if (get_chan_from_user_list(client->chans,chan_name) != NULL)/*Ja' esta' no canal*/
        {
                free(chan_name);
                return;
        }

        if (compic("#unregisted",chan_name))
        {
                if (client->reg != NULL) {
                        write_sock(client,":%s 474 %s %s :Cannot join channel (+b)\r\n",SERVER_NAME,client->nick,chan_name);
                        free(chan_name);
                        return;
                }
        }

        if (compic("#unidentified",chan_name))
        {
                if (client->reg != NULL) if (client->reg->status != USER_STATUS_TO_IDENTIFY) {
                        write_sock(client,":%s 474 %s %s :Cannot join channel (+b)\r\n",SERVER_NAME,client->nick,chan_name);
                        free(chan_name);
                        return;
                }
        }

        if (client->reg == NULL && !compic("#help",chan_name) && !compic("#unregisted",chan_name))
        {
                write_sock(client,":%s 474 %s %s :Cannot join channel (Unregisted)\r\n",SERVER_NAME,client->nick,chan_name);
                write_sock(client,":%s!%s@%s NOTICE %s :Não podes entar em nenhum canal enquanto não te registares\r\n",
                                SERVER_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(chan_name);
                return;
        }

        if (client->reg != NULL) if (client->reg->status == USER_STATUS_TO_IDENTIFY
                                     && !compic("#help",chan_name)
                                     && !compic("#unidentified",chan_name))
        {
                write_sock(client,":%s 474 %s %s :Cannot join channel (Unidentified)\r\n",SERVER_NAME,client->nick,chan_name);
                write_sock(client,":%s!%s@%s NOTICE %s :Não podes entar em nenhum canal enquanto não te identificares\r\n",
                           SERVER_NAME,USER_NAME,SERVER_NAME,client->nick);
                free(chan_name);
                return;
        }

        pchans=get_chan(*ppchans,chan_name);

        if (pchans != NULL)
        {
                /*E' so adicionar, mas a lista de users pode estar vazia...*/

                if (is_banned(client,pchans) && !client->ircop)
                {
                        /*:irc.ptsida.org 474 hehe #coiso :Cannot join channel (+b)*/

                        write_sock(client,":%s 474 %s %s :Cannot join channel (+b)\r\n",SERVER_NAME,client->nick,chan_name);

                        free(chan_name);
                        return;
                }

                if (pchans->reg != NULL && is_akicked(client,pchans->reg->akicks) && !client->ircop)
                {
                        /*:irc.ptsida.org 474 hehe #coiso :Cannot join channel (+b)*/

                        write_sock(client,":%s!%s@%s NOTICE %s :Não podes entrar no canal %s (akick)\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan_name);

                        free(chan_name);
                        return;
                }

                if (pchans->modes.key != NULL && !client->ircop) /*Tem key, e nao e' ircop*/
                {
                        if (tknum(client->msg,' ') < 3)
                        {
                                /*:irc.ptsida.org 475 orium_ #hehe :Cannot join channel (+k)*/

                                write_sock(client,":%s 475 %s %s :Cannot join channel (+k)\r\n",SERVER_NAME,client->nick,chan_name);

                                free(chan_name);
                                return;
                        }
                        key=getoken(client->msg,3,' ');

                        if (!comp(pchans->modes.key,key))
                        {
                                /*:irc.ptsida.org 475 orium_ #hehe :Cannot join channel (+k)*/

                                write_sock(client,":%s 475 %s %s :Cannot join channel (+k)\r\n",SERVER_NAME,client->nick,chan_name);

                                free(chan_name);
                                return;
                        }
                }

                if (pchans->reg != NULL && pchans->reg->set.restricted && get_user_access_level(pchans->reg->ausers,client->nick) <= 0)
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :O teu nivel de acesso ao canal %s não te permite entrar (restricted)\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,chan_name);
                        free(chan_name);
                        return;
                }

                pcusers=alloc_user_in_channel_list(&pchans->users);

                pcusers->user=client;
                pcusers->voice=false;
                pcusers->op=(client->ircop) ? true : false;
                pcusers->secret=secret;

                if (pchans->reg != NULL && get_quant_user_in_chan(pchans) > pchans->reg->users_record) /*record de users*/
                {
                        pchans->reg->users_record=get_quant_user_in_chan(pchans);
                        time(&pchans->reg->record_time);
                }
        }
        else
        {
                pchans=alloc_channel(ppchans);

                pchans->name=chan_name;
                pchans->topic=NULL;
                pchans->topic_set_by=NULL;
                pchans->users=NULL;
                pchans->bans=NULL;
                pchans->reg=NULL;
                pchans->topic_seted=0;

                pchans->modes.t=true;
                pchans->modes.m=false;
                pchans->modes.s=false;
                pchans->modes.c=false;
                pchans->modes.key=NULL;

                time(&(pchans->created));

                if (compic(chan_name,"#unregisted"))
                {
                        pchans->modes.m=true;

                        asprintf(&(pchans->topic),"Canal #unregisted da %s!"
                                                  " Se estás neste canal e' porque o teu nick não esta' registado."
                                                  " Para o registares escreve /%s register <password>."
                                                  " Se precisares de ajuda escreve /join #help",NETWORK_NAME,NICKSERV_NAME);

                        pchans->topic_set_by=malloc(strlen(NICKSERV_NAME)+1);
                        memerror(pchans->topic_set_by);
                        strcpy(pchans->topic_set_by,NICKSERV_NAME);

                        time(&(pchans->topic_seted));

#if MEM_DEBUG
                        debug_fake_malloc(pchans->topic);
#endif
                }

                if (compic(chan_name,"#unidentified"))
                {
                        pchans->modes.m=true;

                        asprintf(&(pchans->topic),"Canal #unidentified da %s!"
                                                  " Se estás neste canal e' porque ainda não te identificas-te."
                                                  " Para te identificares escreve /%s identify <password>."
                                                  " Se precisares de ajuda escreve /join #help",NETWORK_NAME,NICKSERV_NAME);

                        pchans->topic_set_by=malloc(strlen(NICKSERV_NAME)+1);
                        memerror(pchans->topic_set_by);
                        strcpy(pchans->topic_set_by,NICKSERV_NAME);

                        time(&(pchans->topic_seted));

#if MEM_DEBUG
                        debug_fake_malloc(pchans->topic);
#endif
                }

                if (compic(chan_name,"#help"))
                {
                        asprintf(&(pchans->topic),"Canal #help da %s!"
                                                  " Se este canal estiver sem ops põe a tua duvida no #putedo."
                                                  " Se não puderes entrar (por estares desregistado ou não identificado"
                                                  " Tenta falar em PVT com o orium ou vê o help do nickserv: /nickserv help",NETWORK_NAME);

                        pchans->topic_set_by=malloc(strlen(NICKSERV_NAME)+1);
                        memerror(pchans->topic_set_by);
                        strcpy(pchans->topic_set_by,NICKSERV_NAME);

                        time(&(pchans->topic_seted));

#if MEM_DEBUG
                        debug_fake_malloc(pchans->topic);
#endif
                }

                pchans->users=alloc_user_in_channel_list(&pchans->users);

                pchans->users->user=client;
                pchans->users->op=(client->ircop) ? true : false;
                pchans->users->voice=false;
                pchans->users->secret=secret;

                (*channels)++;
                /*Ninguem tem op, quem da' op e' o chanserv ao reservar o canal*/
        }
        /*Se tiver mesmo entrado*/

        puchans=alloc_channel_in_user_list(&client->chans);

        puchans->chan=pchans;
        puchans->op=(client->ircop) ? true : false;
        puchans->voice=false;
        puchans->secret=secret;

        /*:coiso!~orium@irc.ptsida.org JOIN :#chan*/
        write_sock(client,":%s!%s@%s JOIN :%s\r\n",client->nick,USER_NAME,client->md5_ip,chan_name);

        if (pchans->topic != NULL)
        {
                /*
                :irc.ptsida.org 332 orium_ #coiso :ha e tal....
                :irc.ptsida.org 333 orium_ #coiso hehe 1154643855
                */
                write_sock(client,":%s 332 %s %s :%s\r\n",SERVER_NAME,client->nick,chan_name,pchans->topic);
                write_sock(client,":%s 333 %s %s %s %ld\r\n",SERVER_NAME,client->nick,chan_name,pchans->topic_set_by,
                                                             (long)pchans->topic_seted);
        }

        /*Faz o names*/
        free(client->msg);

        client->msg=malloc(strlen(chan_name)+7);
        memerror(client->msg);
        sprintf(client->msg,"NAMES %s",chan_name);

        send_to_all_in_chan(client,chan_name,":%s!%s@%s JOIN :%s\r\n",client->nick,USER_NAME,client->md5_ip,chan_name);
        /*Envia a todos no canal :orium!~orium@irc.ptsida.org JOIN :#chan*/

        names(client);

        if (client->ircop)
        {
                /*:coiso!~orium@irc.ptsida.org MODE #hehehe +o orium_*/
                write_sock(client,":%s!%s@%s MODE %s +o %s\r\n",OPERSERV_NAME,USER_NAME,SERVER_NAME,chan_name,client->nick);
                send_to_all_in_chan(client,":%s!%s@%s MODE %s +o %s\r\n",OPERSERV_NAME,USER_NAME,SERVER_NAME,chan_name,client->nick);
        }
        else if (client->reg != NULL && client->reg->status == USER_STATUS_IDENTIFIED)
                /*O server não deixa ninguem desresgistado entrar num canal, mas...*/
        {
                if (pchans->reg != NULL && get_user_access_level(pchans->reg->ausers,client->nick) >= pchans->reg->levels.autoop)
                {
                        /*:coiso!~orium@irc.ptsida.org MODE #hehehe +o orium_*/
                        write_sock(client,":%s!%s@%s MODE %s +o %s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan_name,client->nick);
                        send_to_all_in_chan(client,":%s!%s@%s MODE %s +o %s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan_name,
                                                                                client->nick);
                        puchans->op=true;
                        pchans->users->op=true;
                }
                else if (pchans->reg != NULL && get_user_access_level(pchans->reg->ausers,client->nick) >= pchans->reg->levels.autovoice)
                {
                        /*:coiso!~orium@irc.ptsida.org MODE #hehehe +v orium_*/
                        write_sock(client,":%s!%s@%s MODE %s +v %s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan_name,client->nick);
                        send_to_all_in_chan(client,":%s!%s@%s MODE %s +v %s\r\n",CHANSERV_NAME,USER_NAME,SERVER_NAME,chan_name,
                                                                                client->nick);
                        puchans->voice=true;
                        pchans->users->voice=true;
                }
                /*
                if (get_user_access_level(pchans->reg->ausers,client->nick) > 0) {
                        time(&pchans->reg->last_use);
                }
                */
        }
}

void who(CLIENTS *client)
{
        char *chan;
        struct user_chans_list *chan_lst;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick WHO :Not enough parameters*/
                write_sock(client,":%s 461 %s WHO :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        chan=getoken(client->msg,2,' ');

        chan_lst=get_chan_from_user_list(client->chans,chan);

        if (chan_lst != NULL)
        {
                struct chan_users_list *usrs_lst=chan_lst->chan->users;

                while (usrs_lst != NULL)
                {
                        if (!usrs_lst->secret)
                        {
                                if (usrs_lst->op)
                                        write_sock(client,":%s 352 %s %s %s %s %s %s H@ :0 %s\r\n",SERVER_NAME,client->nick,chan,USER_NAME,
                                                usrs_lst->user->md5_ip,SERVER_NAME,usrs_lst->user->nick,usrs_lst->user->name);
                                else if (usrs_lst->voice)
                                        write_sock(client,":%s 352 %s %s %s %s %s %s H+ :0 %s\r\n",SERVER_NAME,client->nick,chan,USER_NAME,
                                                usrs_lst->user->md5_ip,SERVER_NAME,usrs_lst->user->nick,usrs_lst->user->name);
                                else
                                        write_sock(client,":%s 352 %s %s %s %s %s %s H :0 %s\r\n",SERVER_NAME,client->nick,chan,USER_NAME,
                                                usrs_lst->user->md5_ip,SERVER_NAME,usrs_lst->user->nick,usrs_lst->user->name);
                        }
                        usrs_lst=usrs_lst->next;
                }
        }

        write_sock(client,":%s 315 %s %s :End of /WHO list.\r\n",SERVER_NAME,client->nick,chan);
/*
        :irc.ptsida.org 352 coiso #chan ~orium irc.ptsida.org irc.ptsida.org orium H :0 Diogo Sousa
        :irc.ptsida.org 352 coiso #chan ~orium irc.ptsida.org irc.ptsida.org coiso H@ :0 Diogo Sousa
        :irc.ptsida.org 315 coiso #chan :End of /WHO list.
*/
        free(chan);
}

void names(CLIENTS *client)
{
        char *chan;
        struct user_chans_list *chan_lst;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick NAMES :Not enough parameters*/
                write_sock(client,":%s 461 %s NAMES :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        chan=getoken(client->msg,2,' ');

        chan_lst=get_chan_from_user_list(client->chans,chan);

        write_sock(client,":%s 353 %s = %s :",SERVER_NAME,client->nick,chan);
        if (chan_lst != NULL)
        {
                struct chan_users_list *usrs_lst=chan_lst->chan->users;

                while (usrs_lst != NULL)
                {
                        if (!usrs_lst->secret)
                        {
                                if (usrs_lst->op)
                                        write_sock(client,"@%s ",usrs_lst->user->nick);
                                else if (usrs_lst->voice)
                                        write_sock(client,"+%s ",usrs_lst->user->nick);
                                else
                                        write_sock(client,"%s ",usrs_lst->user->nick);
                        }
                        usrs_lst=usrs_lst->next;
                }

        }
        write_sock(client,"\r\n");

        write_sock(client,":%s 366 %s %s :End of /NAMES list.\r\n",SERVER_NAME,client->nick,chan);
/*
:irc.ptsida.org 353 coiso = #putedo :coiso PTsida
:irc.ptsida.org 366 coiso #putedo :End of /NAMES list.
*/
        free(chan);
}

void part(CLIENTS *client, CHANS **chans_, bool kick, long *channels, bool force)
{
        char *chan;
        struct user_chans_list *chans_list;
        struct chan_users_list *user_in_chan;
        CHANS *chans;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick PART :Not enough parameters*/

                if (!kick)
                        write_sock(client,":%s 461 %s PART :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        if (client->chans == NULL)
                return;

        chan=getoken(client->msg,2,' ');
        chans_list=get_chan_from_user_list(client->chans,chan);

        if (chans_list == NULL)
        {
                /*:irc.ptsida.org 403 orium_ #coiso :No such channel*/

                if (!kick && !force)
                        write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,chan);
                free(chan);
                return;
        }

#if FORCE_NO_PART_PUTEDO == 1
        if (!kick)
        {
                if (compic(chan,"#putedo"))
                {
                        write_sock(client,":%s 403 %s #putedo :Cannot part #putedo\r\n",SERVER_NAME,client->nick);

                        write_sock(client,":%s!%s@%s NOTICE %s :Não podes sair do #putedo\r\n",
                                   SERVER_NAME,USER_NAME,SERVER_NAME,client->nick);

                        /*rejoin*/
                        write_sock(client,":%s!%s@%s JOIN :#putedo\r\n",client->nick,USER_NAME,client->md5_ip);

                        if (chans_list->chan->topic != NULL)
                        {
                                write_sock(client,":%s 332 %s #putedo :%s\r\n",SERVER_NAME,client->nick,chans_list->chan->topic);
                                write_sock(client,":%s 333 %s #putedo %s %ld\r\n",SERVER_NAME,client->nick,chans_list->chan->topic_set_by,
                                        (long)chans_list->chan->topic_seted);
                        }

                        free(client->msg);

                        client->msg=malloc(32);
                        memerror(client->msg);
                        strcpy(client->msg,"NAMES #putedo");

                        names(client);
                        if (client->ircop)
                                write_sock(client,":%s!%s@%s MODE #putedo +o %s\r\n",OPERSERV_NAME,USER_NAME,SERVER_NAME,client->nick);

                        free(chan);
                        return;
                }
        }
#endif

        if (!kick && !force)
        {
                if (compic(chan,"#unregisted") && client->reg == NULL && !client->ircop)
                {
                        write_sock(client,":%s 403 %s #unregisted :Cannot part #unregisted while unregisted\r\n",SERVER_NAME,client->nick);

                        write_sock(client,":%s!%s@%s NOTICE %s :Não podes sair do #unregisted enquanto não tiveres registado\r\n",
                                   SERVER_NAME,USER_NAME,SERVER_NAME,client->nick);

                        /*rejoin*/
                        write_sock(client,":%s!%s@%s JOIN :#unregisted\r\n",client->nick,USER_NAME,client->md5_ip);

                        if (chans_list->chan->topic != NULL)
                        {
                                write_sock(client,":%s 332 %s #unregisted :%s\r\n",SERVER_NAME,client->nick,chans_list->chan->topic);
                                write_sock(client,":%s 333 %s #unregisted %s %ld\r\n",SERVER_NAME,client->nick,
                                           chans_list->chan->topic_set_by,(long)chans_list->chan->topic_seted);
                        }

                        free(client->msg);

                        client->msg=malloc(32);
                        memerror(client->msg);
                        strcpy(client->msg,"NAMES #unregisted");

                        names(client);

                        free(chan);
                        return;
                }

                if (compic(chan,"#unidentified") && client->reg != NULL && client->reg->status == USER_STATUS_TO_IDENTIFY && !client->ircop)
                {
                        write_sock(client,":%s 403 %s #unidentified :Cannot part #unidentified while unidentified\r\n",SERVER_NAME,
                                   client->nick);

                        write_sock(client,":%s!%s@%s NOTICE %s :Não podes sair do #unidentified enquanto não tiveres identificado\r\n",
                                   SERVER_NAME,USER_NAME,SERVER_NAME,client->nick);

                        /*rejoin*/
                        write_sock(client,":%s!%s@%s JOIN :#unidentified\r\n",client->nick,USER_NAME,client->md5_ip);

                        if (chans_list->chan->topic != NULL)
                        {
                                write_sock(client,":%s 332 %s #unidentified :%s\r\n",SERVER_NAME,client->nick,chans_list->chan->topic);
                                write_sock(client,":%s 333 %s #unidentified %s %ld\r\n",SERVER_NAME,client->nick,
                                           chans_list->chan->topic_set_by,(long)chans_list->chan->topic_seted);
                        }

                        free(client->msg);

                        client->msg=malloc(32);
                        memerror(client->msg);
                        strcpy(client->msg,"NAMES #unidentified");

                        names(client);

                        free(chan);
                        return;
                }
        }

        chans_list=client->chans;

        rm_channel_in_user_list(&client->chans,chans_list);
        free(chans_list);

        chans=get_chan(*chans_,chan);

        if (chans != NULL)
        {
                user_in_chan=get_user_in_chan(chans,client->nick);

                rm_user_in_channel_list(&chans->users,user_in_chan);
        }

        if (!kick)
        {
                send_to_all_in_chan(client,chan,":%s!%s@%s PART %s\r\n",client->nick,USER_NAME,client->md5_ip,chan);
                write_sock(client,":%s!%s@%s PART %s\r\n",client->nick,USER_NAME,client->md5_ip,chan);
        }

        if (chans->users == NULL)
        {
                if (client->reg != NULL && client->reg->status == USER_STATUS_IDENTIFIED && chans->reg != NULL
                    && get_user_access_level(chans->reg->ausers,client->nick) > 0)
                        time(&chans->reg->last_use);

                check_chans(chans_,channels);
        }

        free(chan);
}

void oper(CLIENTS *client, long *ircops, IRCOPS *ircops_lst)
{
        MD5_CTX state;
        unsigned char rcvd_digest[16];
        unsigned char *user_digest;
        char *user;
        char *pass;
        bool ok=false;
        bool verf_digest=false;

        if (tknum(client->msg,' ') < 3)
        {
                /*:server 461 nick OPER :Not enough parameters*/
                write_sock(client,":%s 461 %s OPER :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        user=getoken(client->msg,2,' ');
        pass=getoken(client->msg,3,' ');

        MD5Init(&state);
        MD5Update(&state,(unsigned char *)pass,strlen(pass));
        MD5Final(rcvd_digest,&state);

        user_digest=get_md5_hash_from_ircop(ircops_lst,user);

        if (user_digest != NULL)
                verf_digest=true;

        if (verf_digest)
        {
                short counter=0;
                short matches=0;

                while (counter < 16)
                {
                        if (rcvd_digest[counter] == user_digest[counter])
                                matches++;
                        counter++;
                }

                if (matches == 16)
                        ok=true;
        }

        if (ok)
        {
                struct user_chans_list *chans_lst=client->chans;
                struct chan_users_list *users_lst;

                while (chans_lst != NULL)
                {
                        chans_lst->op=true;
                        chans_lst->voice=false;

                        /*agora tem de ir procurar dentro do canal*/
                        users_lst=chans_lst->chan->users;
                        while (users_lst != NULL)
                        {
                                if (compic(client->nick,users_lst->user->nick))
                                {
                                        users_lst->op=true;
                                        users_lst->voice=false;
                                        break;
                                }
                                users_lst=users_lst->next;
                        }

                        /*informa do op*/
                        write_sock(client,":%s!%s@%s MODE %s +o %s\r\n",
                                                           OPERSERV_NAME,USER_NAME,SERVER_NAME,chans_lst->chan->name,client->nick);

                        send_to_all_in_chan(client,chans_lst->chan->name,":%s!%s@%s MODE %s +o %s\r\n",
                                                                  OPERSERV_NAME,USER_NAME,SERVER_NAME,chans_lst->chan->name,client->nick);


                        chans_lst=chans_lst->next;
                }

                write_sock(client,":%s 381 %s :You are now an IRC Operator\r\n",SERVER_NAME,client->nick);

                if (client->reg != NULL)
                        client->reg->never_unreg=true;

                if (!client->ircop)
                        (*ircops)++;

                client->ircop=true;
        }
        else
        {
                /*:irc.ptsida.org 491 hehe :No O-lines for your host*/
                write_sock(client,":%s 491 %s :No O-lines for your host\r\n",SERVER_NAME,client->nick);
                client->ident_failed++;
        }

        free(user);
        free(pass);
}

void topic(CLIENTS *client, CHANS *chans)
{
        struct user_chans_list *chan_list;
        char *chan_name;

        if (tknum(client->msg,' ') < 3)
        {
                /*:server 461 nick TOPIC :Not enough parameters*/
                write_sock(client,":%s 461 %s TOPIC :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }
        /*
        << TOPIC #coiso :ha e tal....
        >> :hehe!~orium@irc.ptsida.org TOPIC #coiso :ha e tal....
        */
        chan_name=getoken(client->msg,2,' ');

        chan_list=get_chan_from_user_list(client->chans,chan_name);

        if (chan_list != NULL)
        {
                if (!chan_list->op && chan_list->chan->modes.t)
                        /*:irc.ptsida.org 482 hehe #coiso :You're not channel operator*/
                        write_sock(client,":%s 482 %s %s :You're not channel operator\r\n",SERVER_NAME,client->nick,
                                                                                           chan_list->chan->name);
                else
                {
                        char *topic;

                        if (chan_list->chan->reg != NULL && chan_list->chan->reg->set.topic_lock) {
                                write_sock(client,":%s!%s@%s NOTICE %s :Topiclock on\r\n",
                                        CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick);
                                return;
                        }


                        topic=get_irc_str(client->msg,3);

                        if (topic == NULL) {
                                free(chan_name);
                                return;
                        }

                        /*:hehe!~orium@irc.ptsida.org TOPIC #coiso :ha e tal....*/
                        write_sock(client,":%s!%s@%s TOPIC %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,chan_list->chan->name,topic);

                        send_to_all_in_chan(client,chan_list->chan->name,":%s!%s@%s TOPIC %s :%s\r\n",
                                            client->nick,USER_NAME,client->md5_ip,chan_list->chan->name,topic);

                        free(chan_list->chan->topic);
                        free(chan_list->chan->topic_set_by);

                        chan_list->chan->topic=realloc(topic,strlen(topic)+1);
                        memerror(chan_list->chan->topic);

                        chan_list->chan->topic_set_by=malloc(strlen(client->nick)+1);
                        memerror(chan_list->chan->topic_set_by);
                        strcpy(chan_list->chan->topic_set_by,client->nick);

                        time(&chan_list->chan->topic_seted);
                }

        }

        free(chan_name);
}

void ison(CLIENTS **ppclients, CLIENTS *client)
{
        CLIENTS *pclients;
        char *nick;
        long counter=3;

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick ISON :Not enough parameters*/
                write_sock(client,":%s 461 %s ISON :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        write_sock(client,":%s 303 %s :",SERVER_NAME,client->nick);
        /* :irc.ptsida.org 303 orium :|_-GoD-_|*/
        nick=getoken(client->msg,2,' ');
        while (nick != NULL)
        {
                pclients=*ppclients;
                while (pclients != NULL)
                {
                        if (compic(nick,pclients->nick)) {
                                write_sock(client,"%s ",pclients->nick);
                                break;
                        }
                        pclients=pclients->next;
                }

                free(nick);
                nick=getoken(client->msg,counter++,' ');
        }

        write_sock(client,"\r\n");
}

void list(CLIENTS *client, CHANS *chans)
{
        CHANS *chans_lst=chans;
        long users;
        struct chan_users_list *users_lst;

/* se não for secreto e' que incrementa o user, não mostra canais com 0 users
>> :irc.ptsida.org 321 coiso Channel :Users  Name
>> :irc.ptsida.org 322 coiso #coiso 1 :hehehe
>> :irc.ptsida.org 322 coiso #ajuda 1 :
>> :irc.ptsida.org 322 coiso #help 1 :
>> :irc.ptsida.org 322 coiso #PTsida 1 :
>> :irc.ptsida.org 322 coiso #portugal 2 :
>> :irc.ptsida.org 323 coiso :End of /LIST
*/
        write_sock(client,":%s 321 %s Channel :Users  Name\r\n",SERVER_NAME,client->nick);

        while (chans_lst != NULL)
        {
                users=0;
                users_lst=chans_lst->users;

                while (users_lst != NULL)
                {
                        if (!users_lst->secret)
                                users++;

                        users_lst=users_lst->next;
                }

                if (users && !chans_lst->modes.s)
                        write_sock(client,":%s 322 %s %s %ld :%s\r\n",SERVER_NAME,client->nick,chans_lst->name,users,
                                                                (chans_lst->topic != NULL) ? chans_lst->topic : "");

                chans_lst=chans_lst->next;
        }

        write_sock(client,":%s 323 %s :End of /LIST\r\n",SERVER_NAME,client->nick);
}

void kill_(CLIENTS **ppclients, CLIENTS *client)
{
        CLIENTS *pclients;
        char *nick;

        if (!client->ircop)
        {
                /*:irc.ptsida.org 481 x :Permission Denied: Insufficient privileges*/
                write_sock(client,":%s 481 %s :Permission Denied: Insufficient privileges\r\n",SERVER_NAME,client->nick);
                return;
        }

        if (tknum(client->msg,' ') < 2)
        {
                /*:server 461 nick KILL :Not enough parameters*/
                write_sock(client,":%s 461 %s KILL :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }


        nick=getoken(client->msg,2,' ');

        pclients=get_client(*ppclients,nick);

        if (pclients == NULL)
        {
                /*:irc.ptsida.org 401 x c :No such nick*/
                write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,client->nick,nick);
                free(nick);
                return;
        }
        else
        {
                if (pclients->ircop)
                {
                        write_sock(client,":%s 401 %s %s :Cannot kill a ircop\r\n",SERVER_NAME,client->nick,nick);
                        free(nick);
                        return;
                }
                else
                {
/* recebido pelo que mata
NAO >> :irc.ptsida.org NOTICE * :*** Notice -- Received KILL message for killed[~orium@irc.ptsida.org]. From x Path: irc.ptsida.org!x (motivo)
>> :killed!~orium@irc.ptsida.org QUIT :Killed (x (motivo))
*/
/* recebido pelo morto
>> :x!~orium@irc.ptsida.org KILL killed :x (motivo)
>> ERROR :Closing Link: killed[~orium@irc.ptsida.org] by x (Killed (x (motivo)))
*/
/*
Recebido por todos
:killed!~orium@irc.ptsida.org QUIT :Killed (x (motivo))
*/
                        if (tknum(client->msg,' ') > 2)
                        {
                                char *msg;

                                msg=get_irc_str(client->msg,3);

                                if (msg == NULL)
                                {
                                        free(nick);
                                        return;
                                }

                                write_sock(pclients,":%s!%s@%s KILL killed :%s (%s)\r\n",
                                           pclients->nick,USER_NAME,pclients->md5_ip,client->nick,msg);
                                write_sock(pclients,"ERROR :Closing Link: %s[%s@%s] by %s (Killed (%s (%s)))\r\n",
                                           pclients->nick,USER_NAME,pclients->md5_ip,client->nick,client->nick,msg);

                                send_to_all(pclients,":%s!%s@%s QUIT :Killed (%s (%s))\r\n",pclients->nick,USER_NAME,pclients->md5_ip,
                                                                                            client->nick,msg);
                                close(pclients->sd);
                                pclients->error=-1;
                                free(msg);
                        }
                        else
                        {
                                write_sock(pclients,":%s!%s@%s KILL killed :%s\r\n",
                                           pclients->nick,USER_NAME,pclients->md5_ip,client->nick);
                                write_sock(pclients,"ERROR :Closing Link: %s[%s@%s] by %s (Killed (%s))\r\n",
                                           pclients->nick,USER_NAME,pclients->md5_ip,client->nick,client->nick);

                                send_to_all(pclients,":%s!%s@%s QUIT :Killed (%s)\r\n",pclients->nick,USER_NAME,pclients->md5_ip,
                                            client->nick);
                                close(pclients->sd);

                                pclients->error=-1;
                        }
                }
        }

        free(nick);
}

void kick(CLIENTS *client, CHANS **chans_, CLIENTS **ppclients, long *channels)
{
        CLIENTS *client_lst;
        char *chan;
        char *nick;
        char *msg;
        struct user_chans_list *chans_list;

        if (tknum(client->msg,' ') < 3)
        {
                write_sock(client,":%s 461 %s KICK :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        nick=getoken(client->msg,3,' ');

        chan=getoken(client->msg,2,' ');

        chans_list=get_chan_from_user_list(client->chans,chan);

        if (chans_list == NULL)
        {
                /*:irc.ptsida.org 403 orium_ #coiso :No such channel*/
                write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,chan);
                free(chan);
                free(nick);
                return;
        }

        if (!chans_list->op)
        {
                /*:irc.ptsida.org 482 espectador #putedo :You're not channel operator*/
                write_sock(client,":%s 482 %s %s :You're not channel operator\r\n",SERVER_NAME,client->nick,chan);
                free(chan);
                free(nick);
                return;
        }

        client_lst=get_client(*ppclients,nick);

        if (client_lst == NULL)
        {
                /*:irc.ptsida.org 401 orium kfdgtrf :No such nick*/
                write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,client->nick,nick);
                free(chan);
                free(nick);
                return;
        }

        if (client_lst->ircop)
        {
                write_sock(client,":%s 401 %s %s :Cannot kick an ircop\r\n",SERVER_NAME,client->nick,nick);
                free(chan);
                free(nick);
                return;
        }

#if PREVENT_HOSTIL_KICK == 1
        if (chans_list->chan->reg != NULL && !client->ircop) /*Verificação de nivel*/
        {
                veryshort tlevel=get_user_access_level(chans_list->chan->reg->ausers,nick);

                if (get_user_access_level(chans_list->chan->reg->ausers,client->nick) < tlevel
                    && tlevel >= chans_list->chan->reg->levels.autoop
                    && !client->ircop) /*bloquei kick a niveis superiores (mas so' se o alvo tiver opreg)*/
                {
                        write_sock(client,":%s!%s@%s NOTICE %s :O nivel de acesso do user %s ao canal %s não te permite kicka-lo\r\n",
                                   CHANSERV_NAME,USER_NAME,SERVER_NAME,client->nick,nick,chan);

                        free(chan);
                        free(nick);
                        return;
                }
        }
#endif

        if (tknum(client->msg,' ') == 3)
        {
                /*kickador = orium
                  kickado  = espectador*/
                /*:orium!~orium@irc.ptsida.org KICK #hehe espectador :espectador*/
                write_sock(client_lst,":%s!%s@%s KICK %s %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,chan,nick,nick);
                send_to_all_in_chan(client_lst,chan,":%s!%s@%s KICK %s %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,chan,nick,nick);
        }
        else
        {
                char *kmsg;

                kmsg=get_irc_str(client->msg,4);
                if (kmsg == NULL)
                {
                        free(chan);
                        free(nick);
                        return;
                }

                write_sock(client_lst,":%s!%s@%s KICK %s %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,chan,nick,kmsg);
                send_to_all_in_chan(client_lst,chan,":%s!%s@%s KICK %s %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,chan,nick,kmsg);

                free(kmsg);
        }

        msg=client_lst->msg;

        client_lst->msg=malloc(strlen(chan)+6); /*PART #CHAN*/

        sprintf(client_lst->msg,"PART %s",chan);

        part(client_lst,chans_,true,channels,false);

        client_lst->msg=msg;

        free(chan);
        free(nick);
}

void away(CLIENTS *client)
{
        if (tknum(client->msg,' ') < 2) /*Retira o away*/
        {
                if (client->away != NULL)
                {
                        /*:irc.ptsida.org 305 nick :You are no longer marked as being away*/
                        write_sock(client,":%s 305 %s :You are no longer marked as being away\r\n",SERVER_NAME,client->nick);
                        free(client->away);
                        client->away=NULL;
                }
                return;
        }

        free(client->away);

        client->away=get_irc_str(client->msg,2);

        write_sock(client,":%s 306 %s :You have been marked as being away\r\n",SERVER_NAME,client->nick);
        /*:irc.ptsida.org 306 nick :You have been marked as being away*/
}

void help(CLIENTS *client)
{
        /*:irc.ptsida.org NOTICE new :WHOWAS*/
        write_sock(client,":%s NOTICE %s :AWAY\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :DIE\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :HELP\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :INFO\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :INVITE\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :ISON\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :JOIN\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :KICK\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :KILL\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :LIST\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :LUSERS\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :MODE\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :MOTD\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :NAMES\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :NICK\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :NOTICE\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :OPER\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :PART\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :PASS\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :PING\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :PONG\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :PRIVMSG\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :QUIT\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :TOPIC\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :USER\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :USERHOST\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :VERSION\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :WALLOPS\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :WHO\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
        write_sock(client,":%s NOTICE %s :WHOIS\r\n",SERVER_NAME,(client->nick == NULL) ? NO_NICK_RESPRESENTATION : client->nick);
}

void invite(CLIENTS *client, CLIENTS *clients_lst)
{
        CLIENTS *invited;
        struct user_chans_list *user_chan_lst;
        char *chan;
        char *nick;

        if (tknum(client->msg,' ') < 3)
        {
                write_sock(client,":%s 461 %s INVITE :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        /*INVITE invited #x*/
        nick=getoken(client->msg,2,' ');
        chan=getoken(client->msg,3,' ');

        user_chan_lst=get_chan_from_user_list(client->chans,chan);

        if (user_chan_lst == NULL) /*E' contra a RFC mas eu acho melhor...*/
        {
                /*:irc.ptsida.org 403 orium #fg :No such channel*/
                write_sock(client,":%s 403 %s %s :No such channel\r\n",SERVER_NAME,client->nick,chan);
                free(nick);
                free(chan);
                return;
        }

        if (!user_chan_lst->op)
        {
                write_sock(client,":%s 482 %s %s :You're not channel operator\r\n",SERVER_NAME,client->nick,chan);
                free(nick);
                free(chan);
                return;
        }

        invited=get_client(clients_lst,nick);

        if (invited == NULL)
        {
                /*:irc.ptsida.org 401 orium invitedlll :No such nick*/
                write_sock(client,":%s 401 %s %s :No such nick\r\n",SERVER_NAME,client->nick,nick);
                free(nick);
                free(chan);
                return;
        }

        user_chan_lst=get_chan_from_user_list(invited->chans,chan);

        if (user_chan_lst != NULL) /*User ja' entrou*/
        {
                /*:irc.ptsida.org 443 orium invited #x :is already on channel*/
                write_sock(client,":%s 443 %s %s %s :is already on channel\r\n",SERVER_NAME,client->nick,nick,chan);
                free(nick);
                free(chan);
                return;
        }

        /*:irc.ptsida.org 341 orium invited #x*/
        write_sock(client,":%s 341 %s %s %s\r\n",SERVER_NAME,client->nick,nick,chan);
        /*:orium!~orium@irc.ptsida.org INVITE invited :#x*/
        write_sock(invited,":%s!%s@%s INVITE %s :%s\r\n",client->nick,USER_NAME,client->md5_ip,nick,chan);

        free(nick);
        free(chan);
}

void die(CLIENTS *client, CLIENTS **pclients, IRCOPS *ircops)
{
        CLIENTS *pmclient;
        MD5_CTX state;
        unsigned char rcvd_digest[16];
        unsigned char *user_digest;
        short counter=0;
        short matches=0;
        char *pass;
        time_t now;

        if (!client->ircop)
        {
                /*:irc.ptsida.org 481 coiso :Permission Denied: Insufficient privileges*/
                write_sock(client,":%s 481 %s :Permission Denied: Insufficient privileges\r\n",SERVER_NAME,client->nick);
                return;
        }

        if (tknum(client->msg,' ') < 2)
        {
                write_sock(client,":%s 461 %s DIE :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        user_digest=get_md5_hash_from_ircop(ircops,"__die__");

        pass=getoken(client->msg,2,' ');

        MD5Init(&state);
        MD5Update(&state,(unsigned char *)pass,strlen(pass));
        MD5Final(rcvd_digest,&state);

        while (counter < 16)
        {
                if (rcvd_digest[counter] == user_digest[counter])
                        matches++;
                counter++;
        }

        if (matches == 16)
        {
                /*:irc.ptsida.org NOTICE * :*** Notice -- shutdown: Terminated by coiso!~orium@irc.ptsida.org
                  :irc.ptsida.org NOTICE * :*** Notice -- Server terminating.
                */
                pmclient=*pclients;

                while (pmclient != NULL)
                {
                        write_sock(pmclient,":%s NOTICE * :*** Notice -- shutdown: Terminated by %s!%s@%s\r\n",
                                   SERVER_NAME,client->nick,USER_NAME,client->md5_ip);

                        pmclient=pmclient->next;
                }

                pmclient=*pclients;

                time(&now);
                while (pmclient != NULL)
                {
                        write_sock(pmclient,":%s NOTICE * :*** Notice -- Server terminating.\r\n",SERVER_NAME);
                        close(pmclient->sd);

                        if (pmclient->reg != NULL) if (pmclient->reg->status == USER_STATUS_IDENTIFIED)
                        {
                                pmclient->reg->online_time+=now-pmclient->reg->last_time;
                                pmclient->reg->last_time=now;
                                pmclient->reg->status=USER_STATUS_OFFLINE;
                                pmclient->reg->user=NULL;

                                save_userreg(pmclient->reg);
                        }

                        pmclient=pmclient->next;
                }

                raise(SIGKILL);
        }
        else
        {
                write_sock(client,":%s 481 %s :Permission Denied: Wrong Password\r\n",SERVER_NAME,client->nick);
                return;
        }
}

void mode(CLIENTS *client, CHANS *chans, long *ircops)
{
        char *tmp;

        if (tknum(client->msg,' ') < 2)
        {
                write_sock(client,":%s 461 %s MODE :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        tmp=getoken(client->msg,2,' ');

        if (tmp[0] == '#')
                chan_modes(client,chans);
        else
                user_modes(client,ircops);

        free(tmp);
}

void wallops(CLIENTS *client, CLIENTS *clients)
{
        CLIENTS *clients_lst=clients;
        char *msg;

        if (!client->ircop)
        {
                /*:irc.ptsida.org 481 coiso :Permission Denied: Insufficient privileges*/
                write_sock(client,":%s 481 %s :Permission Denied: Insufficient privileges\r\n",SERVER_NAME,client->nick);
                return;
        }

        if (tknum(client->msg,' ') < 2)
        {
                write_sock(client,":%s 461 %s WALLOPS :Not enough parameters\r\n",SERVER_NAME,client->nick);
                return;
        }

        msg=get_irc_str(client->msg,2);
        if (msg == NULL)
                return;

        while (clients_lst != NULL)
        {
                if (clients_lst->ircop)
                        /*:orium!~orium@irc.ptsida.org WALLOPS :* xxx*/
                        write_sock(clients_lst,":%s!%s@%s WALLOPS :* %s\r\n",client->nick,USER_NAME,client->md5_ip,msg);

                clients_lst=clients_lst->next;
        }
        free(msg);
}


char *strip_msg(char *msg) /*Apaga cores, cores de fundo, bolds e underlines*/
{
        char *ret;
        int counter=0;
        int ret_counter=0;
        bool last_w_col=false;
        bool cpy=true;
        int color_nums=0;

        ret=malloc(strlen(msg)+1);
        memerror(ret);

        while (msg[counter] != '\0')
        {
                if (msg[counter] != 31 /*underline*/ && msg[counter] != 2 /*bold*/)
                {
                        if (last_w_col)
                        {
                                if (msg[counter] >= '0' && msg[counter] <= '9') /*se for numero*/
                                {
                                        color_nums++;

                                        if (color_nums > 2)
                                        {
                                                last_w_col=false;
                                                cpy=true;
                                                color_nums=0;
                                        }
                                        else
                                        {
                                                cpy=false;
                                        }
                                }
                                else
                                {
                                        if (msg[counter] == ',' && color_nums <= 2)
                                                last_w_col=true;
                                        else
                                        {
                                                last_w_col=false;
                                                cpy=true;
                                        }
                                        color_nums=0;
                                }
                        }

                        if (msg[counter] == 3 /*color*/)
                        {
                                last_w_col=true;
                                cpy=false;
                        }

                        if (cpy)
                                ret[ret_counter++]=msg[counter];
                }

                counter++;
        }
        ret[ret_counter]='\0';

        return ret;
}
